package vethbr

import (
	"fmt"
	"log"
	"net"

	"github.com/vishvananda/netlink"
)

// 网桥设备搭建容器互通网络
// 实现 Driver 接口
type BridgeDriver struct {
}

func (d *BridgeDriver) Name() string {
	return "bridge"
}

// 创建一个可用网络
// @param string subnet 192.168.2.0/24
// @param string name br0
func (d *BridgeDriver) Create(subnet string, name string) (*Network, error) {
	// 如果该网桥已经存在，则报错
	// 激活网桥 -- ip link set br0 up
	_, err := netlink.LinkByName(name)
	if err == nil { // 存在
		log.Printf("network:%s exist already", name)
		return nil, fmt.Errorf("network:%s exist already", name)
	}

	_, ipnet, err := net.ParseCIDR(subnet)
	if err != nil {
		log.Printf("Failed to ParseCIDR: %v\n", err)
		return nil, err
	}
	n := &Network{
		Name:    name,
		IPRange: ipnet,
		Driver:  d.Name(),
	}

	// 创建网桥 -- 相当于 brctl addbr br0
	bridge := &netlink.Bridge{
		LinkAttrs: netlink.LinkAttrs{
			Name: name,
			MTU:  1500,
		},
	}
	if err := netlink.LinkAdd(bridge); err != nil {
		log.Printf("Failed to create bridge: %v\n", err)
		return nil, err
	}
	// 激活网桥 -- ip link set br0 up
	brFd, err := netlink.LinkByName(name)
	if err != nil {
		log.Printf("Failed to get bridge: %v\n", err)
		return nil, err
	}
	if err := netlink.LinkSetUp(brFd); err != nil {
		log.Printf("Failed to set bridge up: %v\n", err)
		return nil, err
	}

	// 给网桥设置IP段的首地址作为网关
	// 获取该网段第一个没有使用的，理论上是1，如果不是一般说明是配置有问题
	// 创建一个新变量，不能直接使用ipnet，甚至都不能使用 ipnet.IP（切片也是指针）避免指针引用导致的修改混乱
	_, gatewayIPNet, _ := net.ParseCIDR(subnet)
	ipHandler := NewSimpleIPHandler()
	gatewayIP, err := ipHandler.Allocate(gatewayIPNet)
	if err != nil {
		log.Printf("Failed to allocate ip: %v\n", err)
		return nil, err
	}
	gatewayIPNet.IP = gatewayIP
	addr := &netlink.Addr{IPNet: gatewayIPNet}
	if err := netlink.AddrAdd(brFd, addr); err != nil {
		log.Printf("Failed to AddrAdd: %v\n", err)
		ipHandler.Release(gatewayIPNet, gatewayIP)
		return nil, err
	}
	n.GatewayIP = gatewayIP

	// 设置iptables SNAT 和  DNAT 规则，否则无法把网桥上的外网请求转发出去了
	if err := setupIPTablesSNAT(name, ipnet); err != nil {
		log.Printf("Failed to setupIPTablesSNAT : %v\n", err)
		ipHandler.Release(gatewayIPNet, gatewayIP)
		return nil, err
	}
	log.Printf("[create]network dump:%s\n", n.IPRange.IP.String())
	return n, nil
}

// 删除网络
// 不再提供多容器共享网络的服务
func (d *BridgeDriver) Delete(network *Network) error {
	// 根据名字找到对应的Bridge设备
	br, err := netlink.LinkByName(network.Name)
	if err != nil {
		return err
	}

	// 删除网络对应的 Linux Bridge 设备
	err = netlink.LinkDel(br)
	if err != nil {
		return err
	}

	// 释放掉占据的IP
	ipHandler := NewSimpleIPHandler()
	return ipHandler.Release(network.IPRange, network.GatewayIP)
}

// 连接端点到指定网络之中  - brctl addif br0 veth1(这个命令较老了，应该用更先进的 ip link set veth1 master br0)
// 实际就是将 veth-pairs 的奇数端插入网卡
// 此处是 endpoint 中包含着 veth-pairs
// @param Network network  调用方应该保证 network 的驱动处于可用状态
// @param *Endpoint endpoint 调用方应该保证 endpoint 的veth-pairs 已经创建好了
func (d *BridgeDriver) Connect(network *Network, endpoint *Endpoint) error {
	// 获取刚才创建的网桥
	brFd, err := netlink.LinkByName(network.Name)
	if err != nil {
		log.Printf("Failed to get driver: %v\n", err)
		return err
	}

	// 获取veth-pairs的对端（我们把对端插入网桥，把主端放入容器）
	vethPeerFd, err := netlink.LinkByName(endpoint.Device.PeerName)
	if err != nil {
		log.Printf("Failed to get veth-pairs peer: %v\n", err)
		return err
	}

	// 将peer端插入bridge
	if err := netlink.LinkSetMaster(vethPeerFd, brFd); err != nil {
		log.Printf("Failed to set LinkSetMaster(vethPeerFd, brFd): %v\n", err)
		return err
	}

	// 将peer端激活
	if err := netlink.LinkSetUp(vethPeerFd); err != nil {
		log.Printf("Failed to set LinkSetUp(vethPeerFd): %v\n", err)
		return err
	}

	log.Printf("Bridge:%s bind veth-pairs peer:%s", network.Name, endpoint.Device.PeerName)
	return nil
}

// 把某个端点从网络中移除
func (d *BridgeDriver) Disconnect(network *Network, endpoint *Endpoint) error {
	// 获取刚才创建的网桥
	/*
		brFd, err := netlink.LinkByName(network.Name)
		if err != nil {
			log.Printf("Failed to get driver: %v\n", err)
			return err
		}
	*/
	// 获取veth-pairs的对端（我们把对端插入网桥，现在把对端拿出来）
	vethPeerFd, err := netlink.LinkByName(endpoint.Device.PeerName)
	if err != nil {
		log.Printf("Failed to get veth-pairs peer: %v\n", err)
		return err
	}
	err = netlink.LinkSetNoMaster(vethPeerFd)
	if err != nil {
		log.Printf("Failed to LinkSetNoMaster(vethPeerFd): %v\n", err)
		return err
	}
	return nil
}
